package org.tissou.server.Base;

import org.tissou.server.Utils.InterfaceMySQL;
import org.tissou.client.Services.MonException;
import org.tissou.server.Utils.ComparateurApproximatif;
import org.tissou.server.Utils.Variables_Globales;

import java.sql.SQLException;
import java.sql.ResultSet;
import java.util.logging.Logger;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Comparator;

import org.tissou.client.Services.CategorieInfos;

/**
 * Cette petite classe sait gerer une categorie.
 * On peut aussi charger toute une categorie et sa descendance en faisant :
 * Categorie.chargeage(int numCategorie, InterfaceMySql)
 * La categorie numero 0 est la mere de toutes les categories.
 * Elle s'appelle Tout. Du coup, pour charger toutes les categories, on fait :
 * Categorie.chargeage(0)
 */
public class Categorie {

  private static final Logger LOG = Logger.getLogger(Categorie.class.getName());

  private CategorieInfos infos;

  public Categorie(CategorieInfos infos) {
    this.infos = infos;
  }

  public static Categorie chargeage(int quelleCategorie, InterfaceMySQL interfacePourLesRequetes) {
    return new Categorie(chargeageInterne(quelleCategorie, interfacePourLesRequetes));
  }

  private static CategorieInfos chargeageInterne(int quelleCategorie, InterfaceMySQL interfacePourLesRequetes) {
    LOG.info("chargeage de la categorie "+quelleCategorie);
    String requete = String.format("SELECT NoCategorie, NomCategorie, Parent FROM Categories WHERE NoCategorie = '%s'",
        quelleCategorie);

    // Chopper tous les enfants  (categories avec pour parent celle la)
    String requeteEnfants = String.format("SELECT DISTINCT(NoCategorie) FROM Categories WHERE Parent = '%d' ",
        quelleCategorie);
    try {
      ResultSet resultat = interfacePourLesRequetes.RecupererDonnees(requete);
      resultat.next();
      int noCategorie = resultat.getInt(1);
      String nomCategorie = resultat.getString(2);
      int numParent = resultat.getInt(3);

      ResultSet resultatEnfants = interfacePourLesRequetes.RecupererDonnees(requeteEnfants);

      // Liste avec le numero de categorie de tous les enfants de cette categorie
      List<Integer> tousLesEnfants = new ArrayList<Integer>();
      while (resultatEnfants.next()) {
        tousLesEnfants.add(resultatEnfants.getInt(1));
      }

      List<CategorieInfos> categorieEnfants = new ArrayList<CategorieInfos>();
      for (int categorie : tousLesEnfants) {
        // Et hop, chargeage recursif (trop fort dis donc).
        categorieEnfants.add(Categorie.chargeageInterne(categorie, interfacePourLesRequetes));
      }

      return new CategorieInfos(nomCategorie, noCategorie,  categorieEnfants);
    } catch (SQLException e) {
      throw new MonException("Impossible de charger la categorie " + quelleCategorie, e);
    }
  }

  public CategorieInfos rechercherCategorieDansDescendanceAide(int noCategorie, CategorieInfos categorie) {
    if (categorie.getNumero() == noCategorie) {
      return categorie;
    } else {
      for (CategorieInfos enfant : categorie.getEnfants()) {
        CategorieInfos recherche = rechercherCategorieDansDescendanceAide(noCategorie, enfant);
        if (recherche != null) {
          return recherche;
        }
      }
    }
    return null;
  }

    public CategorieInfos getInfos() {
        return infos;
    }

    public Categorie rechercherCategorieDansDescendance(int noCategorie) {
     return new Categorie(rechercherCategorieDansDescendanceAide(noCategorie, infos));
  }

  public Categorie rechercherCategorieDansDescendance(String categorie) {
    return new Categorie(
            rechercherCategorieDansDescendance(categorie, Integer.MAX_VALUE, infos, null, new ComparateurApproximatif()).categorie);
  }

  private static class CategorieAndScore {
    private CategorieAndScore(CategorieInfos categorie, int score) {
      this.categorie = categorie;
      this.score = score;
    }

    public CategorieInfos categorie;
    public int score;
  }

  public CategorieAndScore rechercherCategorieDansDescendance(String categorie, int miniDistance,
       CategorieInfos pourCategorie, CategorieInfos miniDistanceCategorie, Comparator<String> comparator) {

    int score = Math.abs(comparator.compare(pourCategorie.getNom(), categorie));

    if (score < miniDistance) {
      miniDistance = score;
      miniDistanceCategorie = pourCategorie;
    }
    for (CategorieInfos enfant : pourCategorie.getEnfants()) {
      CategorieAndScore recherche = rechercherCategorieDansDescendance(categorie, miniDistance,  enfant, miniDistanceCategorie, comparator);
      if (recherche.score < miniDistance) {
        miniDistance = recherche.score;
        miniDistanceCategorie = recherche.categorie;
      }
    }
    return new CategorieAndScore(miniDistanceCategorie, miniDistance);
  }

  /**
   * Ajoute une categorie comme dernier enfant de la categorie en cours.
   * Ce qui est mega top, c'est que ca ajoute aussi dans la base de donnees.
   * @param nom Nom de la nouvelle categorie.
   * @param interfacePourLesRequetes pour faire les requetes MySql
   */
  public void ajouterCategorieCommeEnfant(String nom, InterfaceMySQL interfacePourLesRequetes) {
    ajouterCategorieCommeEnfantAide(nom, infos, interfacePourLesRequetes);
  }

  /**
   * Ajoute une categorie comme dernier enfant de la categorie en cours.
   * Ce qui est mega top, c'est que ca ajoute aussi dans la base de donnees.
   * @param nom Nom de la nouvelle categorie.
   * @param infos
   * @param interfacePourLesRequetes pour faire les requetes MySql
   */
  public void ajouterCategorieCommeEnfantAide(String nom, CategorieInfos infos, InterfaceMySQL interfacePourLesRequetes) {
    // ceci est pour verifier que aucune des categories enfants du meme parent n'ont le meme nom que la nouvelle categorie.
//    for (CategorieInfos enfant : infos.getEnfants()) {
//      if (enfant.getNom().equalsIgnoreCase(nom)) {
//        throw new MonException("Pas moyen. Ya deja une sous categorie avec le meme nom mec.");
//      }
//    }

    // ceci est pour verifier qu'aucune des categories n'a le meme nom que la nouvelle:
    for (CategorieInfos categorie : Variables_Globales.Tout.getInfos().getEnfants()){
       if (categorie.getNom().equalsIgnoreCase(nom)) {
        throw new MonException("Pas moyen. Ya deja une sous categorie avec le meme nom mec.");
      }
    }

    // Chopper le dernier numero :
    int numero = -1;
    try {
      ResultSet resultat = interfacePourLesRequetes.RecupererDonnees("SELECT MAX(NoCategorie) FROM Categories");
      resultat.next();
      numero = resultat.getInt(1) + 1;
    } catch (SQLException e) {
      LOG.warning("Ca va pas tarder de planter a mon avis, vu qu'on a pas reussi a recuperer le prochain numero.");
    }

    CategorieInfos nouvelle = new CategorieInfos(nom, numero);
    infos.AjouterEnfant(nouvelle);

    // ajouter dans MySql
    try {
      interfacePourLesRequetes.EnvoyerQuery(String.format(
        "INSERT INTO Categories VALUES ('%d', '%s', '%d')", numero, nom, infos.getNumero()));
    } catch (SQLException e) {
      throw new MonException("Impossible de fabriquer la nouvelle categorie. Mega chelou.", e);
    }
  }

  public Categorie trouverCategorieQuiColleBien(String nomApproximatif) {

    return null;
  }

}
